﻿using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.Build.Construction;
using System;
using System.IO;
using System.Collections.Generic;
using ProjectClone.Tool;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis;
using System.Linq;

namespace ProjectClone.CloneHandle
{
    public class ProjectCloneHandler
    {
        private IServiceProvider _serviceProvider;
        /// <summary>
        /// Vs解决方案接口
        /// </summary>
        private IVsSolution _solution;

        private Solution2 _solution2;
        /// <summary>
        /// IDE顶层对象
        /// </summary>
        private DTE2 _dte2;
        /// <summary>
        /// 源解决方案路径
        /// </summary>
        private string _sourceFullName;
        /// <summary>
        /// 源解决方案名称
        /// </summary>
        private string _sourceName;
        /// <summary>
        /// 目标解决方案目录路径
        /// </summary>
        private string _destinationDir;
        /// <summary>
        /// 目标解决方案名称
        /// </summary>
        private string _destinationName;

        private List<ProjectInSolution> _sProject = new List<ProjectInSolution> { };

        private List<ProjectInSolution> _dProject = new List<ProjectInSolution> { };

        private Dictionary<string, SolutionFolder> _folderDic = new Dictionary<string, SolutionFolder> { };

        private List<dynamic> _ModifyCsprojList = new List<dynamic> { };

        public ProjectCloneHandler(ProjectCloneOption option)
        {
            this._solution = option.Solution;
            this._dte2 = option.DTE2;
            this._sourceFullName = option.SourceFullName;
            this._sourceName = Path.GetFileNameWithoutExtension(option.SourceFullName);
            this._destinationDir = option.DestinationDir;
            this._destinationName = option.DestinationName;
            this._serviceProvider = option.ServiceProvider;
            this._solution2 = _dte2.Solution as Solution2;
        }

        /// <summary>
        /// 从源解决方案创建副本
        /// </summary>
        private void CreateCopyBySource()
        {

            if (string.IsNullOrWhiteSpace(_sourceFullName))
            {
                throw new BizException("源解决方案不能为空");
            }

            if (string.IsNullOrWhiteSpace(_destinationDir))
            {
                throw new BizException("目标解决方案目录不能为空");
            }

            if (string.IsNullOrWhiteSpace(_destinationName))
            {
                throw new BizException("目标解决方案名不能为空");
            }
            //存在目录
            if (Directory.Exists(_destinationDir))
            {
                throw new BizException($"目标文件夹下已存在名为{_destinationName}的文件夹");
            }

            var _solutionFile = SolutionFile.Parse(_sourceFullName);

            for (int i = 0; i < _solutionFile.ProjectsInOrder.Count; i++)
            {
                _sProject.Add(_solutionFile.ProjectsInOrder[i]);
            }

            //解析解决方案文件夹下的文件,读取必要信息
            _solution2.Open(_sourceFullName);

            var fileDic = new Dictionary<string, List<string>> { };

            var sourceProjects = new List<ProjectInfo> { };

            foreach (EnvDTE.Project item in _solution2.Projects)
            {
                sourceProjects.Add(ResolveProject(item));
            }

            _solution2.Close(true);


            Directory.CreateDirectory(_destinationDir);

            //创建新解决方案
            _solution2.Create(_destinationDir, _destinationName);

            var folderDic = new Dictionary<string, string> { };

            //添加项目
            foreach (ProjectInfo item in sourceProjects)
            {
                CreateProject(item);
            }
            //保存解决方案
            _solution2.SaveAs(_destinationDir + "\\" + _destinationName + ".sln");

            _solution2.Close(true);

            _ModifyCsprojList.ForEach(x => XMLHelper.ModifyProjectCsproj(x._dPath, _sourceName, _destinationName, x._oPath, _sourceFullName));
        }

        /// <summary>
        /// 生成解决方案
        /// </summary>
        private void BuildSolution()
        {
            var _destinationFullName = _destinationDir + _destinationName + ".sln";

            _dte2.StatusBar.Text = "正在还原Nuget程序包...";

            //执行nuget还原包命令，需要配置nuget的path环境变量
            var result = DosCommanHelper.ExeCommand(string.Format(@"nuget restore {0}", _destinationFullName));

            _dte2.StatusBar.Text = "Nuget程序包还原完毕，生成项目中...";

            #region IDE编译
            //使用VSIDE进行编译

            //_solution2.Open(_destinationFullName);

            //_solution2.SolutionBuild.SolutionConfigurations.Item("DEBUG").Activate();

            //_solution2.SolutionBuild.Build();
            #endregion

            #region Roslyn编译
            //使用roslyn代替MSBuild完成解决方案编译
            var workSpace = MSBuildWorkspace.Create();
            Microsoft.CodeAnalysis.Solution solution = workSpace.OpenSolutionAsync(_destinationFullName).Result;
            ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
            foreach (ProjectId projectId in projectGraph.GetTopologicallySortedProjects())
            {
                Microsoft.CodeAnalysis.Project project = solution.GetProject(projectId);

                Compilation projectCompilation = project.GetCompilationAsync().Result;

                //分析编译后输出信息
                if (null != projectCompilation && !string.IsNullOrEmpty(projectCompilation.AssemblyName))
                {
                    var diagnostics = projectCompilation.GetDiagnostics();
                    var warnDiagnostics = diagnostics.Where(x => x.Severity == DiagnosticSeverity.Warning).ToArray();
                    var errorDiagnostics = diagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).ToArray();

                    if (errorDiagnostics.Any())
                    {
                        throw new BizException($"项目{project.Name}编译失败，请检查源项目");
                    }
                }
            }

            #endregion

            #region MSBulid编译
            ////（主要问题是无法识别c#高版本语法）
            //var _solutionFile = SolutionFile.Parse(_destinationFullName););
            //for (int i = 0; i < _solutionFile.ProjectsInOrder.Count; i++)
            //{
            //    _dProject.Add(_solutionFile.ProjectsInOrder[i]);
            //}

            //foreach (var item in _dProject)
            //{
            //    DosCommanHelper.ExeCommand(string.Format(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe {0} /t:Rebuild /p:VisualStudioVersion=15.0", item.AbsolutePath));// /p:Configuration=Release
            //}
            #endregion

            _dte2.StatusBar.Text = "项目生成成功";
        }

        /// <summary>
        /// 修改命名空间
        /// </summary>
        private void ModifyNamespace()
        {
            _dte2.StatusBar.Text = "修改项目命名空间中...";

            var _destinationFullName = _destinationDir + _destinationName + ".sln";
            _solution2.Open(_destinationFullName);

            foreach (EnvDTE.Project project in _solution2.Projects)
            {
                var props = project.ProjectItems;
                foreach (EnvDTE.ProjectItem projectItem in project.ProjectItems)
                {
                    var kind = projectItem.Kind;

                    ModifyNamespace(projectItem);
                }
            }

            _dte2.StatusBar.Text = "修改项目命名空间完毕，项目复制完成";
        }

        /// <summary>
        /// 修改命名空间
        /// </summary>
        /// <param name="projectItem"></param>
        private void ModifyNamespace(EnvDTE.ProjectItem projectItem)
        {
            var name = projectItem.Name;

            //文件夹
            if (projectItem.Kind.Equals("{6bb5f8ef-4483-11D3-8bcf-00c04f8ec28c}",
                StringComparison.CurrentCultureIgnoreCase) || name.EndsWith(".asax"))
            {
                foreach (EnvDTE.ProjectItem itemProjectItem in projectItem.ProjectItems)
                {
                    ModifyNamespace(itemProjectItem);
                }
            }
            //项目文件夹
            else if (projectItem.Kind.Equals("{66a26722-8fb5-11d2-aa7e-00c04f688dde}",
                    StringComparison.CurrentCultureIgnoreCase) && projectItem.SubProject != null)
            {
                foreach (EnvDTE.ProjectItem itemProjectItem in projectItem.SubProject.ProjectItems)
                {
                    ModifyNamespace(itemProjectItem);
                }
            }
            //如果能够解析出代码模型
            else if (projectItem.FileCodeModel != null)
            {
                var deleteNameSpace = new List<CodeImport> { };
                var importNameSpace = new List<string> { };
                #region 修改using中的命名空间
                foreach (CodeImport codeElement in (projectItem.FileCodeModel as FileCodeModel2).CodeElements.OfType<CodeImport>())
                {
                    if (codeElement.Namespace.Contains(_sourceName))
                    {
                        importNameSpace.Add(codeElement.Namespace.Replace(_sourceName, _destinationName));
                        deleteNameSpace.Add(codeElement);
                    }
                }
                importNameSpace.ForEach(x => (projectItem.FileCodeModel as FileCodeModel2).AddImport(x));
                deleteNameSpace.ForEach(x => (projectItem.FileCodeModel as FileCodeModel2).Remove(x));
                #endregion

                #region 程序集信息

                //if (name == "AssemblyInfo.cs")
                //{
                //    foreach (CodeAttribute2 codeElement in (projectItem.FileCodeModel as FileCodeModel2).CodeElements.OfType<CodeAttribute2>()
                //        .Where(x => new List<string> { "AssemblyTitle", "AssemblyProduct" }.Contains(x.Name)))
                //    {
                //        foreach (CodeAttributeArgument arg in codeElement.Arguments)
                //        {
                //            arg.Value = arg.Value.Replace(_sourceName, _destinationName);
                //            arg.Delete();
                //        }
                //    }

                //    (projectItem.FileCodeModel as FileCodeModel2).AddAttribute("AssemblyTitle", "");
                //}

                #endregion

                #region 修改类命名空间
                foreach (CodeNamespace codeElement in (projectItem.FileCodeModel as FileCodeModel2).CodeElements.OfType<CodeNamespace>())
                {
                    codeElement.Name = codeElement.Name.Replace(_sourceName, _destinationName);
                }
                #endregion
            }
        }

        //创建项目
        public void CreateProject(ProjectInfo PI)
        {
            if (!string.IsNullOrWhiteSpace(PI.AbsolutePath))
            {
                var OnlyAdd = false;
                //如果添加项目不在解决方案文件同级或子级文件夹，则不复制项目，直接添加到新解决方案
                if (PI.RelativePath.StartsWith("..\\"))
                {
                    OnlyAdd = true;
                }

                var originProjectDirPath = FileHelper.MakeAbsolutePath(Path.GetDirectoryName(_sourceFullName), PI.RelativePath);
                var destinationProjectDirPath = FileHelper.MakeAbsolutePath(_destinationDir, PI.RelativePath.Replace(_sourceName, _destinationName));

                //获取folderKey，用来判断项目是否在解决方案文件夹中
                var folderKey = _folderDic.Keys.FirstOrDefault(x => PI.RelativePath.Contains(x));

                if (!string.IsNullOrWhiteSpace(folderKey))
                {
                    if (OnlyAdd)
                    {
                        _folderDic[folderKey].AddFromFile(PI.AbsolutePath);
                    }
                    else
                    {
                        _folderDic[folderKey].AddFromTemplate(
                            PI.AbsolutePath,
                            Path.GetDirectoryName(destinationProjectDirPath),
                            PI.Name.Replace(_sourceName, _destinationName));
                    }
                }
                else
                {
                    if (OnlyAdd)
                    {
                        _solution2.AddFromFile(PI.AbsolutePath, false);
                    }
                    else
                    {
                        _solution2.AddFromTemplate(
                            PI.AbsolutePath,
                            Path.GetDirectoryName(destinationProjectDirPath),
                            PI.Name.Replace(_sourceName, _destinationName), false);
                    }
                }
                if (!OnlyAdd)
                {
                    _ModifyCsprojList.Add(new { _oPath = originProjectDirPath, _dPath = destinationProjectDirPath });
                }
            }
            else
            {
                var project = _solution2.AddSolutionFolder(PI.Name);
                var folder = (SolutionFolder)project.Object;
                _folderDic.Add(PI.Name, folder);
                if (PI.SubProjects != null)
                {
                    foreach (ProjectInfo pi in PI.SubProjects)
                    {
                        CreateProject(pi);
                    }
                }
                if (PI.SubFiles != null)
                {
                    var _sourceDir = Path.GetDirectoryName(_sourceFullName);

                    PI.SubFiles.Select(x => Path.GetDirectoryName(x.AbsolutePath)).Distinct().ToList()
                        .ForEach(x => FileHelper.CopyOldFilesToNewFile(x, x.Replace(_sourceDir, _destinationDir)));

                    foreach (FileInfo fi in PI.SubFiles)
                    {
                        var newPath = fi.AbsolutePath.Replace(_sourceDir, _destinationDir);
                        if (File.Exists(newPath)) project.ProjectItems.AddFromFile(newPath);
                    }
                }
            }
        }

        //解析源解决方案目录结构
        private ProjectInfo ResolveProject(EnvDTE.Project project, string parentPath = "")
        {
            var projectInfo = new ProjectInfo { };

            projectInfo.Name = project.Name;
            projectInfo.AbsolutePath = project.FullName;
            if (!string.IsNullOrWhiteSpace(project.FullName))
            {
                projectInfo.RelativePath = FileHelper.MakeRelativePath(Path.GetDirectoryName(_sourceFullName) + "\\", project.FullName);
            }
            else
            {
                projectInfo.RelativePath = parentPath == "" ? project.Name + "\\" : parentPath + "\\" + project.Name + "\\";
            }

            if (project.Kind.Equals("{66a26720-8fb5-11d2-aa7e-00c04f688dde}", StringComparison.CurrentCultureIgnoreCase))
            {
                var subProjects = new List<ProjectInfo> { };
                var subFiles = new List<FileInfo> { };
                foreach (EnvDTE.ProjectItem item in project.ProjectItems)
                {
                    if (item.Kind.Equals("{66a26722-8fb5-11d2-aa7e-00c04f688dde}", StringComparison.CurrentCultureIgnoreCase) && item.SubProject != null)
                    {
                        subProjects.Add(ResolveProject(item.SubProject, project.Name));
                    }
                    else
                    {
                        subFiles.Add(new FileInfo
                        {
                            Name = item.Name,
                            AbsolutePath = item.FileNames[1]
                        });
                    }
                }
                projectInfo.SubProjects = subProjects;
                projectInfo.SubFiles = subFiles;
            }

            return projectInfo;
        }

        /// <summary>
        /// 执行项目克隆
        /// </summary>
        public void Excute()
        {
            try
            {
                this.CreateCopyBySource();
                this.BuildSolution();
                this.ModifyNamespace();
            }
            catch (BizException e)
            {
                VsShellUtilities.ShowMessageBox(
                    this._serviceProvider,
                    e.Message,
                    "提示",
                    OLEMSGICON.OLEMSGICON_INFO,
                    OLEMSGBUTTON.OLEMSGBUTTON_OK,
                    OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
            }
        }
    }
}
